import re
from typing import List, Dict, Any, Tuple

# 已命中节点
matched_nodes = set()

# 节点定义： (regex, msg, mark, children: List[Node])
# children 为空时停止匹配，用当前 msg/mark 输出
RuleNode = Tuple[str, str, str, List['RuleNode']]

def analyze_symbolize_log(inputs, no_symbolize_nodes, outputs):
    """
    统一分析接口：按顺序调用 3 条规则，逐行扫描日志并收集结果。
    :param inputs: 日志每行列表
    :param outputs: 日志每行分析结果
    """
    for i, line in enumerate(inputs):
        # 检查每个规则
        check_rule_symbolize(outputs, inputs, i, line)
        check_rule_infer_fail(outputs, inputs, i, line, no_symbolize_nodes)
        check_rule_infer_unsupport(outputs, inputs, i, line, no_symbolize_nodes)

def check_rule_symbolize(output, lines, i, line):
    """规则1:Symbolize 失败"""
    if 'Symbolize graph input failed' in line:
        pattern = r'\[ERROR\].*Symbolize: ErrorNo:?\s*\d+'
        error_line = find_error_line(lines, i, pattern)
        if error_line:
            output.append(["", "", "symbolize", "符号泛化失败", error_line, ""])

def check_rule_infer_fail(output, lines, i, line, nodes):
    """规则2:InferOneNode 推导失败"""
    match = re.search(r'\[InferOneNode\] failed,name\[(.*?)\]', line)
    if match and match.group(1) in nodes: 
        op_name = match.group(1)
        pattern = r'\[ERROR\].*(InferOneNode|DoInferAndUpdate): ErrorNo:?\s*\d+'
        error_line = find_error_line(lines, i, pattern)
        if error_line:
            output.append([op_name, "", "symbolize", "符号推导失败", error_line, ""])
            matched_nodes.add(op_name) 

def check_rule_infer_unsupport(output, lines, i, line, nodes):
    """规则3:InferOneNode 不支持"""
    rule_tree : List[RuleNode] = [
        # 根规则：InferOneNode unsupport
        # 第一层子规则1:符号推导函数不存在
        (
            r'We did not find infer symbol shape for op\[{}\]',
            "符号推导函数不存在",
            "确认 op 是否注册推导函数",
            []
        ),
        # 第一层子规则2:存在子图或输入Shape不合规
        (
            r'op {}\[{}\] Check support infer symbol shape failed.',
            "存在子图或输入Shape不合规",
            "需要排查子图或 input origin shape",
            []
        ),
        # 第一层子规则3:输入的符号不存在
        (
            r'Symbol infer unsupported, node {}\[{}\].',
            "输入的符号不存在",
            "确认输入的 symbol shape 为空",
            [
                # 第二层子规则：symbolic value is nullptr
                (
                    r'Symbol Infer unsupported.*symbolic value is nullptr.*node\s*{}',
                    "输入的符号值为空",
                    "确认输入的 symbol value 为空",
                    []
                )
            ]
        )
    ]
    match = re.search(r'InferOneNode unsupport, node (.*?)\[(.*?)\]', line)
    if match and match.group(1) in nodes: 
        op_name = match.group(1)
        op_type = match.group(2)
        for root in rule_tree:
            msg, mark, final_line = match_rule_tree(root, op_name, op_type, lines, i)
            if final_line:
                output.append([op_name, op_type, "symbolize", msg, final_line, mark])
                matched_nodes.add(op_name)
                return
        output.append([op_name, op_type, "symbolize", "符号推导不支持", "", "需排查节点支持情况"])

def find_error_line(lines, start_index, pattern):
    """向前查找第一条匹配 pattern 的日志行"""
    for i in range(start_index - 1, -1, -1):
        if re.search(pattern, lines[i]):
            return lines[i].strip()
    return None

def match_rule_tree(node: RuleNode,
                    op_name: str,
                    op_type: str,
                    lines: List[str],
                    start: int) -> Tuple[str, str, str]:
    """
    返回 (final_msg, final_mark, final_line)
    递归向下匹配，直到没有子规则为止。
    """
    regex, msg, mark, children = node
    pat = regex.format(re.escape(op_name), re.escape(op_type))
    line = find_error_line(lines, start, pat)
    if not line:
        return msg, mark, ""

    # 命中后，继续匹配子规则
    for child in children:
        child_msg, child_mark, child_line = match_rule_tree(child, op_name, op_type, lines, start)
        if child_line:  # 子规则命中
            return child_msg, child_mark, child_line

    # 子规则均未命中，返回当前层结果
    return msg, mark, line

# ---------- 主入口 ----------
def cannot_symbolize(inputs,no_symbolize_nodes,outputs):
    global matched_nodes
    matched_nodes.clear()
    analyze_symbolize_log(inputs, no_symbolize_nodes, outputs)
    for node in no_symbolize_nodes:
        if node not in matched_nodes:
            outputs.append([node, "", "symbolize", "未找到日志", "", "未匹配到任何规则"])
    
# Example usage:
# log_file = 'test.log'
# outputs = []
# no_symbolize_nodes = ['Relu_0', 'Relu_7', 'Add', 'Const2', 'Const4']
# with open(log_file, 'r', encoding='utf-8') as file:
#     inputs = file.readlines()
#     cannot_symbolize(inputs,no_symbolize_nodes,outputs)
# print("outputs:")
# for op_name, op_type, mod, err, line, mark, _ in outputs:
#     print(f"{op_name}, {op_type}, {mod}, {err}, {line}, {mark}")